home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gdevpdfc.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  17.5 KB  |  584 lines

  1. /* Copyright (C) 1999, 2000 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: gdevpdfc.c,v 1.12 2000/09/19 19:00:17 lpd Exp $ */
  20. /* Color space management and writing for pdfwrite driver */
  21. #include "math_.h"
  22. #include "memory_.h"
  23. #include "gx.h"
  24. #include "gscspace.h"        /* for gscie.h */
  25. #include "gscdevn.h"
  26. #include "gscie.h"
  27. #include "gscindex.h"
  28. #include "gscsepr.h"
  29. #include "gserrors.h"
  30. #include "gdevpdfx.h"
  31. #include "gdevpdfg.h"
  32. #include "gdevpdfo.h"
  33. #include "stream.h"
  34. #include "strimpl.h"
  35. #include "sstring.h"
  36.  
  37. /* ------ CIE space testing ------ */
  38.  
  39. /* Test whether a cached CIE procedure is the identity function. */
  40. #define cie_cache_is_identity(pc)\
  41.   ((pc)->floats.params.is_identity)
  42. #define cie_cache3_is_identity(pca)\
  43.   (cie_cache_is_identity(&(pca)[0]) &&\
  44.    cie_cache_is_identity(&(pca)[1]) &&\
  45.    cie_cache_is_identity(&(pca)[2]))
  46.  
  47. /*
  48.  * Test whether a cached CIE procedure is an exponential.  A cached
  49.  * procedure is exponential iff f(x) = k*(x^p).  We make a very cursory
  50.  * check for this: we require that f(0) = 0, set k = f(1), set p =
  51.  * log[a](f(a)/k), and then require that f(b) = k*(b^p), where a and b are
  52.  * two arbitrarily chosen values between 0 and 1.  Naturally all this is
  53.  * done with some slop.
  54.  */
  55. #define ia (gx_cie_cache_size / 3)
  56. #define ib (gx_cie_cache_size * 2 / 3)
  57. #define iv(i) ((i) / (double)(gx_cie_cache_size - 1))
  58. #define a iv(ia)
  59. #define b iv(ib)
  60.  
  61. private bool
  62. cie_values_are_exponential(floatp va, floatp vb, floatp k,
  63.                float *pexpt)
  64. {
  65.     double p;
  66.  
  67.     if (fabs(k) < 0.001)
  68.     return false;
  69.     if (va == 0 || (va > 0) != (k > 0))
  70.     return false;
  71.     p = log(va / k) / log(a);
  72.     if (fabs(vb - k * pow(b, p)) >= 0.001)
  73.     return false;
  74.     *pexpt = p;
  75.     return true;
  76. }
  77.  
  78. private bool
  79. cie_scalar_cache_is_exponential(const gx_cie_scalar_cache * pc, float *pexpt)
  80. {
  81.     double k, va, vb;
  82.  
  83.     if (fabs(pc->floats.values[0]) >= 0.001)
  84.     return false;
  85.     k = pc->floats.values[gx_cie_cache_size - 1];
  86.     va = pc->floats.values[ia];
  87.     vb = pc->floats.values[ib];
  88.     return cie_values_are_exponential(va, vb, k, pexpt);
  89. }
  90. #define cie_scalar3_cache_is_exponential(pca, expts)\
  91.   (cie_scalar_cache_is_exponential(&(pca)[0], &(expts).u) &&\
  92.    cie_scalar_cache_is_exponential(&(pca)[1], &(expts).v) &&\
  93.    cie_scalar_cache_is_exponential(&(pca)[2], &(expts).w))
  94.  
  95. private bool
  96. cie_vector_cache_is_exponential(const gx_cie_vector_cache * pc, float *pexpt)
  97. {
  98.     double k, va, vb;
  99.  
  100.     if (fabs(pc->vecs.values[0].u) >= 0.001)
  101.     return false;
  102.     k = pc->vecs.values[gx_cie_cache_size - 1].u;
  103.     va = pc->vecs.values[ia].u;
  104.     vb = pc->vecs.values[ib].u;
  105.     return cie_values_are_exponential(va, vb, k, pexpt);
  106. }
  107. #define cie_vector3_cache_is_exponential(pca, expts)\
  108.   (cie_vector_cache_is_exponential(&(pca)[0], &(expts).u) &&\
  109.    cie_vector_cache_is_exponential(&(pca)[1], &(expts).v) &&\
  110.    cie_vector_cache_is_exponential(&(pca)[2], &(expts).w))
  111.  
  112. #undef ia
  113. #undef ib
  114. #undef iv
  115. #undef a
  116. #undef b
  117.  
  118. /* ------ Color space writing ------ */
  119.  
  120. /* Define standard and short color space names. */
  121. const pdf_color_space_names_t pdf_color_space_names = {
  122.     PDF_COLOR_SPACE_NAMES
  123. };
  124. const pdf_color_space_names_t pdf_color_space_names_short = {
  125.     PDF_COLOR_SPACE_NAMES_SHORT
  126. };
  127.  
  128. /*
  129.  * Create a local Device{Gray,RGB,CMYK} color space corresponding to the
  130.  * given number of components.
  131.  */
  132. int
  133. pdf_cspace_init_Device(gs_color_space *pcs, int num_components)
  134. {
  135.     switch (num_components) {
  136.     case 1: gs_cspace_init_DeviceGray(pcs); break;
  137.     case 3: gs_cspace_init_DeviceRGB(pcs); break;
  138.     case 4: gs_cspace_init_DeviceCMYK(pcs); break;
  139.     default: return_error(gs_error_rangecheck);
  140.     }
  141.     return 0;
  142. }
  143.  
  144. /* Add a 3-element vector to a Cos array or dictionary. */
  145. private int
  146. cos_array_add_vector3(cos_array_t *pca, const gs_vector3 *pvec)
  147. {
  148.     int code = cos_array_add_real(pca, pvec->u);
  149.  
  150.     if (code >= 0)
  151.     code = cos_array_add_real(pca, pvec->v);
  152.     if (code >= 0)
  153.     code = cos_array_add_real(pca, pvec->w);
  154.     return code;
  155. }
  156. private int
  157. cos_dict_put_c_key_vector3(cos_dict_t *pcd, const char *key,
  158.                const gs_vector3 *pvec)
  159. {
  160.     cos_array_t *pca = cos_array_alloc(pcd->pdev, "cos_array_from_vector3");
  161.     int code;
  162.  
  163.     if (pca == 0)
  164.     return_error(gs_error_VMerror);
  165.     code = cos_array_add_vector3(pca, pvec);
  166.     if (code < 0) {
  167.     COS_FREE(pca, "cos_array_from_vector3");
  168.     return code;
  169.     }
  170.     return cos_dict_put_c_key_object(pcd, key, COS_OBJECT(pca));
  171. }
  172.  
  173. /* Create a Separation or DeviceN color space (internal). */
  174. private int
  175. pdf_separation_color_space(gx_device_pdf *pdev,
  176.                cos_array_t *pca, const char *csname,
  177.                const cos_value_t *snames,
  178.                const gs_color_space *alt_space,
  179.                const gs_function_t *pfn,
  180.                const pdf_color_space_names_t *pcsn)
  181. {
  182.     cos_value_t v;
  183.     int code;
  184.  
  185.     if ((code = cos_array_add(pca, cos_c_string_value(&v, csname))) < 0 ||
  186.     (code = cos_array_add_no_copy(pca, snames)) < 0 ||
  187.     (code = pdf_color_space(pdev, &v, alt_space, pcsn, false)) < 0 ||
  188.     (code = cos_array_add(pca, &v)) < 0 ||
  189.     (code = pdf_function(pdev, pfn, &v)) < 0 ||
  190.     (code = cos_array_add(pca, &v)) < 0
  191.     )
  192.     return code;
  193.     return 0;
  194. }
  195.  
  196. /*
  197.  * Create a PDF color space corresponding to a PostScript color space.
  198.  * For parameterless color spaces, set *pvalue to a (literal) string with
  199.  * the color space name; for other color spaces, create a cos_dict_t if
  200.  * necessary and set *pvalue to refer to it.
  201.  */
  202. int
  203. pdf_color_space(gx_device_pdf *pdev, cos_value_t *pvalue,
  204.         const gs_color_space *pcs,
  205.         const pdf_color_space_names_t *pcsn,
  206.         bool by_name)
  207. {
  208.     gs_memory_t *mem = pdev->pdf_memory;
  209.     gs_color_space_index csi = gs_color_space_get_index(pcs);
  210.     cos_array_t *pca;
  211.     cos_dict_t *pcd;
  212.     cos_value_t v;
  213.     const gs_cie_common *pciec;
  214.     gs_function_t *pfn;
  215.     int code;
  216.  
  217.     switch (csi) {
  218.     case gs_color_space_index_DeviceGray:
  219.     cos_c_string_value(pvalue, pcsn->DeviceGray);
  220.     return 0;
  221.     case gs_color_space_index_DeviceRGB:
  222.     cos_c_string_value(pvalue, pcsn->DeviceRGB);
  223.     return 0;
  224.     case gs_color_space_index_DeviceCMYK:
  225.     cos_c_string_value(pvalue, pcsn->DeviceCMYK);
  226.     return 0;
  227.     case gs_color_space_index_Pattern:
  228.     if (!pcs->params.pattern.has_base_space) {
  229.         cos_c_string_value(pvalue, "/Pattern");
  230.         return 0;
  231.     }
  232.     break;
  233.     default:
  234.     break;
  235.     }
  236.     /* Space has parameters -- create an array. */
  237.     pca = cos_array_alloc(pdev, "pdf_color_space");
  238.     if (pca == 0)
  239.     return_error(gs_error_VMerror);
  240.     switch (csi) {
  241.     case gs_color_space_index_CIEA: {
  242.     /* Check that we can represent this as a CalGray space. */
  243.     const gs_cie_a *pcie = pcs->params.a;
  244.     gs_vector3 expts;
  245.  
  246.     if (!(pcie->MatrixA.u == 1 && pcie->MatrixA.v == 1 &&
  247.           pcie->MatrixA.w == 1 &&
  248.           pcie->common.MatrixLMN.is_identity))
  249.         return_error(gs_error_rangecheck);
  250.     if (cie_cache_is_identity(&pcie->caches.DecodeA) &&
  251.         cie_scalar3_cache_is_exponential(pcie->common.caches.DecodeLMN, expts) &&
  252.         expts.v == expts.u && expts.w == expts.u
  253.         ) {
  254.         DO_NOTHING;
  255.     } else if (cie_cache3_is_identity(pcie->common.caches.DecodeLMN) &&
  256.            cie_vector_cache_is_exponential(&pcie->caches.DecodeA, &expts.u)
  257.            ) {
  258.         DO_NOTHING;
  259.     } else
  260.         return_error(gs_error_rangecheck);
  261.     code = cos_array_add(pca, cos_c_string_value(&v, "/CalGray"));
  262.     if (code < 0)
  263.         return code;
  264.     pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
  265.     if (pcd == 0)
  266.         return_error(gs_error_VMerror);
  267.     if (expts.u != 1) {
  268.         code = cos_dict_put_c_key_real(pcd, "/Gamma", expts.u);
  269.         if (code < 0)
  270.         return code;
  271.     }
  272.     pciec = (const gs_cie_common *)pcie;
  273.     }
  274.     cal:
  275.     code = cos_dict_put_c_key_vector3(pcd, "/WhitePoint",
  276.                       &pciec->points.WhitePoint);
  277.     if (code < 0)
  278.     return code;
  279.     if (pciec->points.BlackPoint.u != 0 ||
  280.     pciec->points.BlackPoint.v != 0 ||
  281.     pciec->points.BlackPoint.w != 0
  282.     ) {
  283.     code = cos_dict_put_c_key_vector3(pcd, "/BlackPoint",
  284.                       &pciec->points.BlackPoint);
  285.     if (code < 0)
  286.         return code;
  287.     }
  288.     code = cos_array_add(pca, COS_OBJECT_VALUE(&v, pcd));
  289.     break;
  290.     case gs_color_space_index_CIEABC: {
  291.     /* Check that we can represent this as a CalRGB space. */
  292.     const gs_cie_abc *pcie = pcs->params.abc;
  293.     gs_vector3 expts;
  294.     const gs_matrix3 *pmat;
  295.  
  296.     if (pcie->common.MatrixLMN.is_identity &&
  297.         cie_cache3_is_identity(pcie->common.caches.DecodeLMN) &&
  298.         cie_vector3_cache_is_exponential(pcie->caches.DecodeABC, expts)
  299.         )
  300.         pmat = &pcie->MatrixABC;
  301.     else if (pcie->MatrixABC.is_identity &&
  302.          cie_cache3_is_identity(pcie->caches.DecodeABC) &&
  303.          cie_scalar3_cache_is_exponential(pcie->common.caches.DecodeLMN, expts)
  304.          )
  305.         pmat = &pcie->common.MatrixLMN;
  306.     else
  307.         return_error(gs_error_rangecheck);
  308.     code = cos_array_add(pca, cos_c_string_value(&v, "/CalRGB"));
  309.     if (code < 0)
  310.         return code;
  311.     pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
  312.     if (pcd == 0)
  313.         return_error(gs_error_VMerror);
  314.     if (expts.u != 1 || expts.v != 1 || expts.w != 1) {
  315.         code = cos_dict_put_c_key_vector3(pcd, "/Gamma", &expts);
  316.         if (code < 0)
  317.         return code;
  318.     }
  319.     if (!pmat->is_identity) {
  320.         cos_array_t *pcma =
  321.         cos_array_alloc(pdev, "pdf_color_space(Matrix)");
  322.  
  323.         if (pcma == 0)
  324.         return_error(gs_error_VMerror);
  325.         if ((code = cos_array_add_vector3(pcma, &pmat->cu)) < 0 ||
  326.         (code = cos_array_add_vector3(pcma, &pmat->cv)) < 0 ||
  327.         (code = cos_array_add_vector3(pcma, &pmat->cw)) < 0 ||
  328.         (code = cos_dict_put(pcd, (const byte *)"/Matrix", 7,
  329.                      COS_OBJECT_VALUE(&v, pcma))) < 0
  330.         )
  331.         return code;
  332.     }
  333.     pciec = (const gs_cie_common *)pcie;
  334.     }
  335.     goto cal;
  336.     case gs_color_space_index_Indexed: {
  337.     const gs_indexed_params *pip = &pcs->params.indexed;
  338.     const gs_color_space *base_space =
  339.         (const gs_color_space *)&pip->base_space;
  340.     int num_entries = pip->hival + 1;
  341.     int num_components = gs_color_space_num_components(base_space);
  342.     uint table_size = num_entries * num_components;
  343.     /* Guess at the extra space needed for ASCII85 encoding. */
  344.     uint string_size = 1 + table_size * 2 + table_size / 30 + 2;
  345.     uint string_used;
  346.     byte buf[100];        /* arbitrary */
  347.     stream_AXE_state st;
  348.     stream s, es;
  349.     byte *table =
  350.         gs_alloc_string(mem, string_size, "pdf_color_space(table)");
  351.     byte *palette =
  352.         gs_alloc_string(mem, table_size, "pdf_color_space(palette)");
  353.     gs_color_space cs_gray;
  354.  
  355.     if (table == 0 || palette == 0) {
  356.         gs_free_string(mem, palette, table_size,
  357.                "pdf_color_space(palette)");
  358.         gs_free_string(mem, table, string_size,
  359.                "pdf_color_space(table)");
  360.         return_error(gs_error_VMerror);
  361.     }
  362.     swrite_string(&s, table, string_size);
  363.     s_init(&es, NULL);
  364.     s_init_state((stream_state *)&st, &s_AXE_template, NULL);
  365.     s_init_filter(&es, (stream_state *)&st, buf, sizeof(buf), &s);
  366.     sputc(&s, '<');
  367.     if (pcs->params.indexed.use_proc) {
  368.         gs_client_color cmin, cmax;
  369.         byte *pnext = palette;
  370.  
  371.         int i, j;
  372.  
  373.         /* Find the legal range for the color components. */
  374.         for (j = 0; j < num_components; ++j)
  375.         cmin.paint.values[j] = min_long,
  376.             cmax.paint.values[j] = max_long;
  377.         gs_color_space_restrict_color(&cmin, base_space);
  378.         gs_color_space_restrict_color(&cmax, base_space);
  379.         /*
  380.          * Compute the palette values, with the legal range for each
  381.          * one mapped to [0 .. 255].
  382.          */
  383.         for (i = 0; i < num_entries; ++i) {
  384.         gs_client_color cc;
  385.  
  386.         gs_cspace_indexed_lookup(&pcs->params.indexed, i, &cc);
  387.         for (j = 0; j < num_components; ++j) {
  388.             float v = (cc.paint.values[j] - cmin.paint.values[j])
  389.             * 255 / (cmax.paint.values[j] - cmin.paint.values[j]);
  390.  
  391.             *pnext++ = (v <= 0 ? 0 : v >= 255 ? 255 : (byte)v);
  392.         }
  393.         }
  394.     } else
  395.         memcpy(palette, pip->lookup.table.data, table_size);
  396.     if (gs_color_space_get_index(base_space) ==
  397.         gs_color_space_index_DeviceRGB
  398.         ) {
  399.         /* Check for an all-gray palette. */
  400.         int i;
  401.  
  402.         for (i = table_size; (i -= 3) >= 0; )
  403.         if (palette[i] != palette[i + 1] ||
  404.             palette[i] != palette[i + 2]
  405.             )
  406.             break;
  407.         if (i < 0) {
  408.         /* Change the color space to DeviceGray. */
  409.         for (i = 0; i < num_entries; ++i)
  410.             palette[i] = palette[i * 3];
  411.         table_size = num_entries;
  412.         gs_cspace_init_DeviceGray(&cs_gray);
  413.         base_space = &cs_gray;
  414.         }
  415.     }
  416.     pwrite(&es, palette, table_size);
  417.     gs_free_string(mem, palette, table_size, "pdf_color_space(palette)");
  418.     sclose(&es);
  419.     sflush(&s);
  420.     string_used = (uint)stell(&s);
  421.     table = gs_resize_string(mem, table, string_size, string_used,
  422.                  "pdf_color_space(table)");
  423.     /*
  424.      * Since the array is always referenced by name as a resource
  425.      * rather than being written as a value, even for in-line images,
  426.      * always use the full name for the color space.
  427.      */
  428.     if ((code = pdf_color_space(pdev, pvalue, base_space,
  429.                     &pdf_color_space_names, false)) < 0 ||
  430.         (code = cos_array_add(pca,
  431.             cos_c_string_value(&v, 
  432.                        pdf_color_space_names.Indexed
  433.                        /*pcsn->Indexed*/))) < 0 ||
  434.         (code = cos_array_add(pca, pvalue)) < 0 ||
  435.         (code = cos_array_add_int(pca, pip->hival)) < 0 ||
  436.         (code = cos_array_add_no_copy(pca,
  437.                       cos_string_value(&v, table,
  438.                                string_used))) < 0
  439.         )
  440.         return code;
  441.     }
  442.     break;
  443.     case gs_color_space_index_DeviceN:
  444.     pfn = gs_cspace_get_devn_function(pcs);
  445.     /****** CURRENTLY WE ONLY HANDLE Functions ******/
  446.     if (pfn == 0)
  447.         return_error(gs_error_rangecheck);
  448.     {
  449.         cos_array_t *psna = 
  450.         cos_array_alloc(pdev, "pdf_color_space(DeviceN)");
  451.         int i;
  452.  
  453.         if (psna == 0)
  454.         return_error(gs_error_VMerror);
  455.         for (i = 0; i < pcs->params.device_n.num_components; ++i) {
  456.         code = pdf_separation_name(pdev, &v,
  457.                        pcs->params.device_n.names[i]);
  458.         if (code < 0 ||
  459.             (code = cos_array_add_no_copy(psna, &v)) < 0)
  460.             return code;
  461.         }
  462.         COS_OBJECT_VALUE(&v, psna);
  463.         if ((code = pdf_separation_color_space(pdev, pca, "/DeviceN", &v,
  464.                            (const gs_color_space *)
  465.                     &pcs->params.device_n.alt_space,
  466.                     pfn, &pdf_color_space_names)) < 0)
  467.         return code;
  468.     }
  469.     break;
  470.     case gs_color_space_index_Separation:
  471.     pfn = gs_cspace_get_sepr_function(pcs);
  472.     /****** CURRENTLY WE ONLY HANDLE Functions ******/
  473.     if (pfn == 0)
  474.         return_error(gs_error_rangecheck);
  475.     if ((code = pdf_separation_name(pdev, &v,
  476.                     pcs->params.separation.sname)) < 0 ||
  477.         (code = pdf_separation_color_space(pdev, pca, "/Separation", &v,
  478.                            (const gs_color_space *)
  479.                     &pcs->params.separation.alt_space,
  480.                     pfn, &pdf_color_space_names)) < 0)
  481.         return code;
  482.     break;
  483.     case gs_color_space_index_Pattern:
  484.     if ((code = pdf_color_space(pdev, pvalue,
  485.                     (const gs_color_space *)
  486.                     &pcs->params.pattern.base_space,
  487.                     &pdf_color_space_names, false)) < 0 ||
  488.         (code = cos_array_add(pca,
  489.                   cos_c_string_value(&v, "/Pattern"))) < 0 ||
  490.         (code = cos_array_add(pca, pvalue)) < 0
  491.         )
  492.         return code;
  493.     break;
  494.     default:
  495.     return_error(gs_error_rangecheck);
  496.     }
  497.     /*
  498.      * Register the color space as a resource, since it must be referenced
  499.      * by name rather than directly.
  500.      */
  501.     {
  502.     pdf_resource_t *pres;
  503.  
  504.     code =
  505.         pdf_alloc_resource(pdev, resourceColorSpace, gs_no_id, &pres, 0L);
  506.     if (code < 0) {
  507.         COS_FREE(pca, "pdf_color_space");
  508.         return code;
  509.     }
  510.     pca->id = pres->object->id;
  511.     COS_FREE(pres->object, "pdf_color_space");
  512.     pres->object = (cos_object_t *)pca;
  513.     cos_write_object(COS_OBJECT(pca), pdev);
  514.     }
  515.     if (by_name) {
  516.     /* Return a resource name rather than an object reference. */
  517.     discard(COS_RESOURCE_VALUE(pvalue, pca));
  518.     } else
  519.     discard(COS_OBJECT_VALUE(pvalue, pca));
  520.     return 0;
  521. }
  522.  
  523. /* Create colored and uncolored Pattern color spaces. */
  524. private int
  525. pdf_pattern_space(gx_device_pdf *pdev, cos_value_t *pvalue,
  526.           pdf_resource_t **ppres, const char *cs_name)
  527. {
  528.     if (!*ppres) {
  529.     int code = pdf_begin_resource_body(pdev, resourceColorSpace, gs_no_id,
  530.                        ppres);
  531.  
  532.     if (code < 0)
  533.         return code;
  534.     pprints1(pdev->strm, "%s\n", cs_name);
  535.     pdf_end_resource(pdev);
  536.     (*ppres)->object->written = true; /* don't write at end */
  537.     }
  538.     cos_resource_value(pvalue, (*ppres)->object);
  539.     return 0;
  540. }
  541. int
  542. pdf_cs_Pattern_colored(gx_device_pdf *pdev, cos_value_t *pvalue)
  543. {
  544.     return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[0],
  545.                  "[/Pattern]");
  546. }
  547. int
  548. pdf_cs_Pattern_uncolored(gx_device_pdf *pdev, cos_value_t *pvalue)
  549. {
  550.     int ncomp = pdev->color_info.num_components;
  551.     static const char *const pcs_names[5] = {
  552.     0, "[/Pattern /DeviceGray]", 0, "[/Pattern /DeviceRGB]",
  553.     "[/Pattern /DeviceCMYK]"
  554.     };
  555.  
  556.     return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[ncomp],
  557.                  pcs_names[ncomp]);
  558. }
  559.  
  560. /* ---------------- Miscellaneous ---------------- */
  561.  
  562. /* Set the ProcSets bits corresponding to an image color space. */
  563. void
  564. pdf_color_space_procsets(gx_device_pdf *pdev, const gs_color_space *pcs)
  565. {
  566.     const gs_color_space *pbcs = pcs;
  567.  
  568.  csw:
  569.     switch (gs_color_space_get_index(pbcs)) {
  570.     case gs_color_space_index_DeviceGray:
  571.     case gs_color_space_index_CIEA:
  572.     /* We only handle CIEBasedA spaces that map to CalGray. */
  573.     pdev->procsets |= ImageB;
  574.     break;
  575.     case gs_color_space_index_Indexed:
  576.     pdev->procsets |= ImageI;
  577.     pbcs = (const gs_color_space *)&pcs->params.indexed.base_space;
  578.     goto csw;
  579.     default:
  580.     pdev->procsets |= ImageC;
  581.     break;
  582.     }
  583. }
  584.